package com.googlecode.nls4j;

import java.util.Locale;
import java.util.ResourceBundle;
import java.util.ResourceBundle.Control;

/**
 * This implementation of {@linkplain BundleLoader} is based on
 * {@linkplain ResourceBundleLoader}. The loaded {@linkplain ResourceBundle}s
 * are converted to {@linkplain EnumBundle}s. This loader supports all
 * configuration parameters as {@linkplain ResourceBundleLoader} plus one
 * additional - {@linkplain #isPreferDefaultEnumBundle()}. If this parameter is
 * set to true, the method {@link #getDefaultBundle(Class, Locale)} will use
 * {@linkplain EnumBundle#defaultValuesBundle(Class, Locale)} for creating
 * default value bundles, otherwise -
 * {@linkplain DefaultValuesBundle#getInstance(Locale)}. Setting this parameter
 * to true is preferable when the method
 * {@linkplain NlsKey#getDefaultValue(Locale)} is slower than
 * {@linkplain EnumBundle#get(Enum)}. Here is an example of such case:
 * 
 * <pre>
 * public String getDefaultValue(final Locale locale) {
 *     final String lang = locale.getLanguage();
 * 
 *     if (lang.equals(LANG_RU)) {
 *         return ruDefaultValue;
 *     } else if (lang.equals(LANG_FR)) {
 *         return frDefaultValue;
 *     } else if (lang.equals(LANG_DE)) {
 *         return deDefaultValue;
 *     } else {
 *         return enDefaultValue;
 *     }
 * }
 * </pre>
 * 
 * @author Andrey Pavlenko
 */
public class EnumBundleLoader<K extends Enum<K> & NlsKey<V>, V> extends
        ResourceBundleLoader<K, V> {
    private final boolean preferDefaultEnumBundle;

    /**
     * Same as
     * {@link #EnumBundleLoader(String, ClassLoader, ResourceBundle.Control, boolean)
     * EnumBundleLoader(null, null, null, false)}.
     */
    public EnumBundleLoader() {
        this(null, null, null, false);
    }

    /**
     * Same as
     * {@link #EnumBundleLoader(String, ClassLoader, ResourceBundle.Control, boolean)
     * EnumBundleLoader(null, null, null, preferDefaultEnumBundle)}.
     */
    public EnumBundleLoader(final boolean preferDefaultEnumBundle) {
        this(null, null, null, preferDefaultEnumBundle);
    }

    /**
     * Same as
     * {@link #EnumBundleLoader(String, ClassLoader, ResourceBundle.Control, boolean)
     * EnumBundleLoader(baseName, null, null, false)}.
     */
    public EnumBundleLoader(final String baseName) {
        this(baseName, null, null, false);
    }

    /**
     * Same as
     * {@link #EnumBundleLoader(String, ClassLoader, ResourceBundle.Control, boolean)
     * EnumBundleLoader(baseName, loader, null, false)}.
     */
    public EnumBundleLoader(final String baseName, final ClassLoader loader) {
        this(baseName, loader, null, false);
    }

    /**
     * Create new EnumBundleLoader with the specified parameters. If
     * {@code preferDefaultEnumBundle} is true the method
     * {@link #getDefaultBundle(Class, Locale)} will use
     * {@linkplain EnumBundle#defaultValuesBundle(Class, Locale)}, otherwise -
     * {@linkplain DefaultValuesBundle#getInstance(Locale)}.
     */
    public EnumBundleLoader(final String baseName, final ClassLoader loader,
            final Control control, final boolean preferDefaultEnumBundle) {
        super(baseName, loader, control);
        this.preferDefaultEnumBundle = preferDefaultEnumBundle;
    }

    /**
     * Returns the default EnumBundleLoader (i.e. new
     * {@linkplain #EnumBundleLoader(String, ClassLoader, ResourceBundle.Control, boolean)
     * EnumBundleLoader(null, null, null, preferDefaultEnumBundle)}). This
     * method does not create a new instance on each invocation but returns the
     * cached instance instead.
     */
    @SuppressWarnings("unchecked")
    public static <K extends Enum<K> & NlsKey<V>, V> EnumBundleLoader<K, V> getDefault(
            final boolean preferDefaultEnumBundle) {
        return preferDefaultEnumBundle ? InstanceHolder2.instance
                : InstanceHolder1.instance;
    }

    /**
     * Returns the value of the {@code preferDefaultEnumBundle} parameter
     * specified in the constructor.
     */
    public boolean isPreferDefaultEnumBundle() {
        return preferDefaultEnumBundle;
    }

    /**
     * This implementation returns
     * {@linkplain EnumBundle#defaultValuesBundle(Class, Locale)
     * EnumBundle.defaultValuesBundle(keyType, locale)} if
     * {@linkplain #isPreferDefaultEnumBundle()} is true, otherwise - returns
     * {@linkplain DefaultValuesBundle#getInstance(Locale)
     * DefaultValuesBundle.getInstance(locale)}.
     */
    @Override
    public NlsBundle<K, V> getDefaultBundle(final Class<K> keyType,
            final Locale locale) {
        return isPreferDefaultEnumBundle() ? EnumBundle.defaultValuesBundle(
            keyType, locale) : DefaultValuesBundle.<K, V> getInstance(locale);
    }

    /**
     * Returns {@linkplain EnumBundle#fromResourceBundle(Class, ResourceBundle)
     * EnumBundle.fromResourceBundle(keyType, resourceBundle)}.
     */
    @Override
    protected NlsBundle<K, V> toNlsBundle(final Class<K> keyType,
            final ResourceBundle resourceBundle) {
        return EnumBundle.fromResourceBundle(keyType, resourceBundle);
    }

    private static final class InstanceHolder1 {
        @SuppressWarnings("rawtypes")
        static final EnumBundleLoader instance = new EnumBundleLoader(
                null, null, null, false);
    }

    private static final class InstanceHolder2 {
        @SuppressWarnings("rawtypes")
        static final EnumBundleLoader instance = new EnumBundleLoader(
                null, null, null, true);
    }
}
